home *** CD-ROM | disk | FTP | other *** search
- /*
- * uudebat.c, uubatch version 1.0.5
- *
- * uudebat: batched UUCP de-batcher
- *
- * Authors :
- * Baruch Cochavy (blue@dduster.hellnet.org)
- * Gil Tene (devil@hellnet.org).
- *
- *
- * This program will de-batch an incoming batch. Input file is fed via
- * stdin, and is composed of many UUCP files, with and added single
- * character ('#') at the beginning of each line, each file data section
- * is preceded by the destination filename.
- *
- * This is where security comes in mind. This is one of the main reasons
- * the program is simple - it shouldn't take more then a couple of min. to
- * figure out what the risks running it are. We have tried to cover all
- * possible security risks : Files are only opened in the uucp machine's
- * spool directory. The program checks to see that the destination
- * file name contains no "/" characters, and that the file is a valid
- * uucp file (only X. and D. files are allowed, no C. files are).
- *
- * One may find this to be inadequate, and add some more security, sanity,
- * disk space checking and probably another zilions of checks. Fell free
- * to modify it in any way appropriate, but keep in mind your incoming
- * batched mail will be passing here, so be extra careful ro check it before
- * being installed.
- *
- * Batch File format:
- * ------------------
- * file name #1
- * # line 1 of file 1
- * # line 2 of file 1
- * # .
- * # .
- * # .
- * file name #2
- * # line 1 of file 2
- * # line 2 of file 2
- * # .
- * # .
- * # .
- *
- * In version 1.0.4 and later we have added an optional "escape" line
- * syntax. The purpose of these lines is to allow more data about the
- * batched files to be added in later releases for "smarter" operation
- * (such as data for e-mail non-delivery bounces, etc.).
- * "escape" lines are lines in X. files which start with a "#!UUBATCH :"
- * (in addition to the leading # char.). These lines, if encountered,
- * are filtered out in uudebat 1.0.4 or later. The lines will not be
- * filtered out in earlier versions, but they would also do no harm,
- * since they are in uucp X. file comment format, and will be totaly
- * ignored by uuxqt. Escape line length may NOT exceed 253 characters.
- *
- * We have also defined the future syntax of uubatch escape lines :
- *
- * #!UUBATCH : {uubatch facility} { parameter1 } { parameter2 } ...
- *
- * Where uubatch facility is something like "DATE" (for e-mail date)
- * and each parameter is an ASCII string.
- *
- * All uubatch facility names NOT beginning with X- (e.g. X-FOO) are
- * reserved by us for future use. If people wish to add their own
- * extentions and features using the escape line feature, they should
- * use facility names beginning with X-.
- *
- *
- */
-
- #include <stdio.h>
- #include <string.h>
- #ifdef sun386i
- extern char *getenv();
- #else
- #include <stdlib.h>
- #endif
-
- #ifdef SCO
- #include <limits.h>
- #define PMAX _POSIX_PATH_MAX
- #define index(_a,_b) strchr(_a,_b)
- #else
- #include <sys/param.h>
- #ifdef HAS_STRING
- #include <string.h>
- #else
- #include <strings.h>
- #endif
- #define PMAX MAXPATHLEN
- #endif
-
- #include "patchlevel.h"
-
- /* NOTICE: COMMAND is defined as the system executed string to result in
- * error message being mailed. You may like to change that.
- *
- * This string is used in a sprintf statment, and if changed, should
- * include provisions for one string which represents the file name
- * holding the error text.
- */
-
- #define COMMAND "mail -s uudebat_error_report postmaster < %s\n"
-
- /* UUBATCH escape perfix. Lines in the batch that begin with this prefix
- * won't get written to any output file. This line will be used in future
- * uubatch version to support various enhancments
- */
-
- #define ESCAPE_PREFIX "#!UUBATCH :"
- #define ESCAPE_PREFIX2 "#%UUBATCH :"
-
- #define EXIT_ERROR 1
- #define EXIT_NO_ERROR 0
-
- /* define line_state constants */
-
- #define BEGINING_OF_LINE 0
- #define MIDDLE_OF_LINE 1
- #define ESCAPE_LINE 2
-
- #define str_eq(_s1,_s2) (!strncmp(_s1,_s2,strlen(_s2)))
-
- /* NOTE: the value of LINE_LENGTH below should never be set to anything
- * less then a UUBATCH escape length, which is 12, to avoid spliting
- * the escape sequence amond two lines. Escape lines, however, can
- * bo of any liength.
- */
- #define LINE_LENGTH 8192
-
- static void notify_error();
- static FILE * secure_fopen();
-
- int main()
- {
- int
- line_state, /* state of input line being read: */
-
- error_flag=0, /* error flag for the file being */
- /* created. */
-
- system_error=0; /* global error flag */
-
- char
- filename[PMAX],
- output_filename[PMAX],
- pathname[PMAX],
- line[LINE_LENGTH],
- *t,
- *orig_system;
-
- FILE
- *fo; /* output stream file pointer */
-
-
- /* identify the machine this batch will come into : */
-
- if (!(orig_system = getenv("UU_MACHINE")))
- {
- fprintf(stderr,"uudebat: must know originating mach. name\n");
- exit(255);
- }
-
- /* cd to uucp machine's spool directory : */
-
- sprintf(pathname,"%s/%s",UUCP_SPOOL_DIR,orig_system);
- if (chdir(pathname))
- {
- perror("Cannot cd to node's spool directory");
- exit(254);
- }
-
- /* read the first line in the input stream. This line should */
- /* contain a file name : */
-
- t = fgets(filename, PMAX, stdin);
-
- /* Loop until there are no more lines in stdin : */
-
- while (t)
- {
- error_flag = 0;
-
- /* line_state is used to control the hadling of lines as
- * they are read. UUdebat assumes no limit to line length.
- * And therefor needs the line_state variable to distinguish
- * the following states :
- *
- * BEGINING_OF_LINE - When at the begining of a new line.
- * MIDDLE_OF_LINE - When in the middle of a line. Indicates it is
- * not needed to check for UUDEBAT line headers
- * or UUDEBAT escape line.
- * ESCAPE_LINE - When tossing escape line to the wind.
- */
-
- line_state = BEGINING_OF_LINE;
-
- /* Open the output file based on the requested file name, */
- /* while making sure security is not broken : */
-
- fo = secure_fopen(filename,output_filename,&error_flag);
-
- /* Each input batch line is read, until that file is at end.
- * Each line is stripped of its first character, used to
- * identify it as belonging to that file, and special
- * "escape" comment lines which may have been inserted in
- * X. files are filtered out :
- */
-
- while (t=fgets(line, LINE_LENGTH, stdin))
- {
- if (line_state == BEGINING_OF_LINE ) /* first part of line */
- {
- if ( *t != '#' ) break;
- t++;
- }
-
- if ( line_state == BEGINING_OF_LINE
- && ( str_eq(t, ESCAPE_PREFIX ) || str_eq(t, ESCAPE_PREFIX2) )
- )
- line_state = ESCAPE_LINE;
-
- /* if rest of line, or line does not begin with */
- /* UUBATCH escape, emit it. */
-
- if ( (line_state != ESCAPE_LINE) || (filename[0]!='X') )
- fputs(t,fo);
-
- if ( line_state == BEGINING_OF_LINE )
- line_state = MIDDLE_OF_LINE;
- if ( index(t,'\n') ) line_state = BEGINING_OF_LINE;
- }
-
- /* notify of possible error: */
-
- if (ferror(fo))
- {
- sprintf(line,"Write error(s) on \"%s\"", output_filename);
- perror(line);
- break;
- }
-
- /* since there are no more consecutive lines containing '#'
- * as their first character, this file has ended. We
- * therefore close it and start a new one, by moving the
- * line read into the file name to be opened.
- */
-
- if (fclose(fo)) /* close file, notify of possible error: */
- {
- sprintf(line,"Close error on \"%s\"",output_filename);
- perror(line);
- break;
- }
-
- if (t)
- {
- if (strlen(line) > PMAX)
- {
- fprintf(stderr,"Filename too long. Truncated.\n");
- fprintf(stderr," Original filename: %s\n",line);
- }
- strncpy(filename,line,PMAX);
- filename[PMAX] = '\0';
- }
-
- /* now that the output file is closed, let's see what can we
- * do with it. If it's creation or writing met with some
- * error, this is the time to tell somebody about it :
- */
-
- if (error_flag)
- notify_error(output_filename);
-
- /* make sure the exit status reflects an error if one
- * occurs :
- */
- system_error = ( error_flag ? EXIT_ERROR : EXIT_NO_ERROR );
- }
-
- exit(system_error);
- }
-
- /* notify_error : notify someone of an error, sending the data file... :
- */
- static void
- notify_error(output_filename)
- char *output_filename;
- {
- int i;
- char line[256];
- sprintf(line, COMMAND, output_filename);
- if (i = system(line))
- fprintf(stderr, "uudebat: system error %d\n",i);
- unlink(output_filename);
- }
-
- /* secure_fopen : Open the output file based on the requested file name,
- * while making sure security is not broken :
- */
- static FILE *
- secure_fopen(req_filename,output_filename,error_flag)
- char *req_filename,*output_filename;
- int *error_flag;
- {
- FILE
- *fo;
- char
- line[128],
- *t;
-
- if ( t=index( req_filename, '\n' ) ) *t='\0';
-
- /* make sure file name begins with "D." or "X." : */
-
- if ((req_filename[0] != 'D' && req_filename[0] != 'X')
- || req_filename[1] != '.')
- {
- fprintf(stderr,"Can only write D. and X. files: %s\n",
- req_filename);
- *error_flag = 4;
- }
-
- /* Check to verify no '/' is embedded. This will make sure the
- * file is written in the spool directory, and not anywhere
- * else ...
- */
-
- if ( index(req_filename, '/') )
- {
- fprintf(stderr,"Cannot write to another directory: %s\n",
- req_filename);
- *error_flag = 5;
- }
-
- /* form the output filename : either it is the requested filename
- * (remember that we ARE already cd'ed to the system's uucp spool
- * directory), or we get a temporary filename if the requested
- * one is not legal :
- */
-
- if (*error_flag)
- tmpnam(output_filename);
- else
- strcpy(output_filename,req_filename);
-
- /* We now open the output file : */
-
- if (!(fo = fopen(output_filename,"w")))
- {
- sprintf(line,"Cannot open output file \"%s\"", output_filename);
- perror(line);
-
- /* If file was not opened, open a temporary one : */
-
- tmpnam(output_filename);
-
- *error_flag = 6;
-
- if (!(fo = fopen(output_filename,"w")))
- {
- fprintf(stderr,"Second open error in a row ... \n");
- exit(255);
- }
- }
-
- /* if there was an error, the file we are writing to will eventually
- * end up as mail to POSTMASTER, so we now put a header on it :
- */
-
- if (*error_flag)
- {
- fprintf(fo, "\nuudebat: error (%d)\n",*error_flag);
- fprintf(fo, "Original file name requested was \"%s\"\n",
- req_filename);
- fprintf(fo, "\nMail body enclosed.\n");
- fprintf(fo, "======================================\n");
- }
-
- return fo;
- }
-
-